﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace NGuice.Inject.Internal.Util
{
    public static class Preconditions
    {

        public static void CheckArgument(bool expression)
        {
            if (!expression)
            {
                throw new ArgumentException();
            }
        }


        public static void CheckArgument(bool expression, object errorMessage)
        {
            if (!expression)
            {
                throw new ArgumentException(errorMessage.ToString());
            }
        }

        public static void CheckArgument(bool expression,
            String errorMessageTemplate, params object[] errorMessageArgs)
        {
            if (!expression)
            {
                throw new ArgumentException(
                    Format(errorMessageTemplate, errorMessageArgs));
            }
        }

        public static void CheckState(bool expression)
        {
            if (!expression)
            {
                throw new MethodAccessException();
            }
        }

        public static void CheckState(bool expression, object errorMessage)
        {
            if (!expression)
            {
                throw new MethodAccessException(errorMessage.ToString());
            }
        }

        /// <summary>
        /// 检查参数O必须为泛型参数，且泛型参数必须为T的超类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="o"></param>
        public static void CheckSuperGenericParameter<T>(object o)
        {
            Type oType = o.GetType();
            while (!oType.IsGenericType && oType.BaseType != typeof(object))
            {
                oType = oType.BaseType;
            }
            if (!oType.IsGenericType)
            {
                throw new ArgumentException("argument: " + o.ToString() + " must be generic Type!");
            }
            else if (!oType.GetGenericArguments()[0].IsAssignableFrom(typeof(T)))
            {
                throw new ArgumentException("argument: " + o.ToString() + " must has generic parameters Type for super type:" + typeof(T).ToString());
            }
        }

        /// <summary>
        /// 检查参数O必须为泛型参数，且泛型参数必须为T的子类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="o"></param>
        public static void CheckSubGenericParameter<T>(object o)
        {
            Type oType = o.GetType();
            while (!oType.IsGenericType && oType.BaseType != typeof(object))
            {
                oType = oType.BaseType;
            }
            if (!oType.IsGenericType)
            {
                throw new ArgumentException("argument: " + o.ToString() + " must be generic Type!");
            }
            else if (!typeof(T).IsAssignableFrom(oType.GetGenericArguments()[0]))
            {
                throw new ArgumentException("argument: " + o.ToString() + " must has generic parameters Type for sub type:" + typeof(T).ToString());
            }
        }


        public static void CheckState(bool expression, string errorMessageTemplate, params object[] errorMessageArgs)
        {
            if (!expression)
            {
                throw new IllegalStateException(Format(errorMessageTemplate, errorMessageArgs));
            }
        }

        public static T CheckNotNull<T>(T reference)
        {
            if (reference == null)
            {
                throw new ArgumentNullException();
            }
            return reference;
        }

        public static T CheckNotNull<T>(T reference, object errorMessage)
        {
            if (reference == null)
            {
                throw new ArgumentNullException(errorMessage.ToString());
            }
            return reference;
        }

        public static T CheckNotNull<T>(T reference, string errorMessageTemplate,
            params object[] errorMessageArgs)
        {
            if (reference == null)
            {
                // If either of these parameters is null, the right thing happens anyway
                throw new ArgumentNullException(
                    Format(errorMessageTemplate, errorMessageArgs));
            }
            return reference;
        }

        public static IEnumerable<T> CheckContentsNotNull<T>(IEnumerable<T> iterable)
        {
            if (ContainsOrIsNull(iterable))
            {
                throw new ArgumentNullException();
            }
            return iterable;
        }

        public static IEnumerable<T> CheckContentsNotNull<T>(IEnumerable<T> iterable, object errorMessage)
        {
            if (ContainsOrIsNull(iterable))
            {
                throw new ArgumentNullException(errorMessage.ToString());
            }
            return iterable;
        }

        public static IEnumerable<T> CheckContentsNotNull<T>(IEnumerable<T> iterable, string errorMessageTemplate, params object[] errorMessageArgs) 
        {
            if (ContainsOrIsNull(iterable))
            {
                throw new ArgumentNullException(
                    Format(errorMessageTemplate, errorMessageArgs));
            }
            return iterable;
        }

        private static bool ContainsOrIsNull<T>(IEnumerable<T> iterable)
        {
            if (iterable == null)
            {
                return true;
            }

            if (iterable is ICollection<T>)
            {
                ICollection<T> collection = (ICollection<T>)iterable;
                try
                {
                    return collection.Contains(default(T));
                }
                catch (NullReferenceException e)
                {
                    // A NPE implies that the collection doesn't contain null.
                    return false;
                }
            }
            else
            {
                foreach (object element in iterable)
                {
                    if (element == null)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        public static void CheckElementIndex(int index, int size)
        {
            CheckElementIndex(index, size, "index");
        }

        public static void CheckElementIndex(int index, int size, string desc)
        {
            CheckArgument(size >= 0, "negative size: {0}", size);
            if (index < 0)
            {
                throw new IndexOutOfRangeException(
                    Format("{0} ({1}) must not be negative", desc, index));
            }
            if (index >= size)
            {
                throw new IndexOutOfRangeException(
                    Format("{0} ({1}) must be less than size ({2})", desc, index, size));
            }
        }

        public static void CheckPositionIndex(int index, int size)
        {
            CheckPositionIndex(index, size, "index");
        }

        public static void CheckPositionIndex(int index, int size, string desc)
        {
            CheckArgument(size >= 0, "negative size: {0}", size);
            if (index < 0)
            {
                throw new IndexOutOfRangeException(Format(
                    "{0} ({1}) must not be negative", desc, index));
            }
            if (index > size)
            {
                throw new IndexOutOfRangeException(Format(
                    "{0} ({1}) must not be greater than size (%s)", desc, index, size));
            }
        }

        public static void CheckPositionIndexes(int start, int end, int size)
        {
            CheckPositionIndex(start, size, "start index");
            CheckPositionIndex(end, size, "end index");
            if (end < start)
            {
                throw new IndexOutOfRangeException(Format(
                    "end index {0} must not be less than start index {1}", end, start));
            }
        }


        internal static string Format(string template, params object[] args)
        {
            return string.Format(template, args);
        }
    }
}
